/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = ora_tti_pro.c                                      */
/*  NOTE      =                                                    */
/*  DATE      = 2014/10/13 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>

#include "frcomm.h"
#include "frstr.h"
#include "frmem.h"
#include "frlog.h"

#include "conntrack.h"
#include "frct_comm.h"

#include "../ora_core.h"
#include "../comm/ora_comm.h"
#include "../comm/ora_util.h"
#include "ora_mar_engine.h"
#include "ora_tti_pro.h"

#define APM_ORA_PROSVRSTR_MAXLEN 50

FR_STATIC int m_maxCharWidth[][2] = {
        {1, 1}, {2, 1}, {3, 1}, {4, 1}, {5, 1}, {6, 1}, {7, 1}, {8, 1}, {9, 1}, {10, 1}, {11, 1}, {12, 1}, {13, 1}, {14, 1}, {15, 1}, {16, 1}, {17, 1}, {18, 1}, {19, 1}, {20, 1},
        {21, 1}, {22, 1}, {23, 1}, {25, 1}, {27, 1}, {28, 1}, {31, 1}, {32, 1}, {33, 1}, {34, 1}, {35, 1}, {36, 1}, {37, 1}, {38, 1}, {39, 1}, {40, 1}, {41, 1}, {42, 1}, {43, 1},
        {44, 1}, {45, 1}, {46, 1}, {47, 1}, {48, 1}, {49, 1}, {50, 1}, {51, 1}, {61, 1}, {70, 1}, {72, 1}, {81, 1}, {82, 1}, {90, 1}, {91, 1}, {92, 1}, {93, 1}, {94, 1}, {95, 1},
        {96, 1}, {97, 1}, {98, 1}, {99, 1}, {100, 1}, {101, 1}, {110, 1}, {113, 1}, {114, 1}, {140, 1}, {150, 1}, {152, 1}, {153, 1}, {154, 1}, {155, 1}, {156, 1}, {158, 1}, {159, 1},
        {160, 1}, {161, 1}, {162, 1}, {163, 1}, {164, 1}, {165, 1}, {166, 1}, {167, 1}, {170, 1}, {171, 1}, {172, 1}, {173, 1}, {174, 1}, {175, 1}, {176, 1}, {177, 1}, {178, 1},
        {179, 1}, {180, 1}, {181, 1}, {182, 1}, {183, 1}, {184, 1}, {185, 1}, {186, 1}, {187, 1}, {188, 1}, {189, 1}, {190, 1}, {191, 1}, {192, 1}, {193, 1}, {194, 1}, {195, 1},
        {196, 1}, {197, 1}, {198, 1}, {199, 1}, {200, 1}, {201, 1}, {202, 1}, {203, 1}, {204, 1}, {205, 1}, {206, 1}, {207, 1}, {208, 1}, {210, 1}, {211, 1}, {221, 1}, {222, 1},
        {223, 1}, {224, 1}, {225, 1}, {226, 1}, {230, 1}, {231, 1}, {232, 1}, {233, 1}, {235, 1}, {239, 1}, {241, 1}, {251, 1}, {261, 1}, {262, 1}, {263, 1}, {264, 1}, {265, 1},
        {266, 1}, {267, 1}, {277, 1}, {278, 1}, {279, 1}, {301, 1}, {311, 1}, {312, 1}, {314, 1}, {315, 1}, {316, 1}, {317, 1}, {319, 1}, {320, 1}, {322, 1}, {323, 1}, {324, 1},
        {351, 1}, {352, 1}, {353, 1}, {354, 1}, {368, 1}, {380, 1}, {381, 1}, {382, 1}, {383, 1}, {384, 1}, {385, 1}, {386, 1}, {390, 1}, {401, 1}, {500, 1}, {504, 1}, {505, 1},
        {506, 1}, {507, 1}, {508, 1}, {509, 1}, {511, 1}, {514, 1}, {554, 1}, {555, 1}, {556, 1}, {557, 1}, {558, 1}, {559, 1}, {560, 1}, {561, 1}, {563, 1}, {565, 1}, {566, 1},
        {567, 1}, {590, 1}, {798, 1}, {799, 258}, {829, 2}, {830, 3}, {831, 3}, {832, 2}, {833, 3}, {834, 2}, {835, 3}, {836, 2}, {837, 3}, {838, 2}, {840, 2}, {842, 3}, {845, 2},
        {846, 2}, {850, 2}, {851, 2}, {852, 2}, {853, 3}, {854, 4}, {860, 4}, {861, 4}, {862, 2}, {863, 4}, {864, 3}, {865, 2}, {866, 2}, {867, 2}, {868, 2}, {870, 3}, {871, 3},
        {872, 4}, {873, 4}, {994, 2}, {995, 2}, {996, 3}, {997, 2}, {998, 3}, {1001, 258}, {1830, 258}, {1832, 258}, {1833, 258}, {1840, 258}, {1842, 258}, {1850, 258}, {1852, 258},
        {1853, 258}, {1860, 258}, {1863, 260}, {1864, 258}, {1865, 258}, {2000, 258}, {2002, 258}, {9996, 3}, {9997, 3}, {9998, 3}, {9999, 3}
};

/*******************************************************************************
*  FUNC     :  对应于 12c oracle.jdbc.driver.DBConversion类的public static final short findDriverCharSet(short paramShort1, short paramShort2)方法
*  ARGS     :  s_param1                             (参数1)
*  ARGS     :  s_param2                             (参数2)
*  RTN      :  s                                    (返回charset值)
*  NOTE     :
*******************************************************************************/
FR_STATIC short __find_driver_charset(short s_param1
                                , short s_param2)
{
    short s = 0;
    switch (s_param1) {
        case 1:
        case 2:
        case 31:
        case 178:
        case 873:
            s = s_param1;
            break;
        default:
            s = (short) (s_param2 >= 8030 ? 871 : 870);
    }
    return s;
}

/*******************************************************************************
*  FUNC     :  对应于 12c oracle.jdbc.driver.DBConversion类的public static final boolean isCharSetMultibyte(short paramShort)方法
*  ARGS     :  s_param                             (charset值)
*  RTN      :  0                                    (不是multibyte)
*              1                                    (是multibyte)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __is_charset_multibyte(short s_param) {
    switch (s_param) {
        case 1:
        case 31:
            return 0;
        case -5:
        case -1:
        case 870:
        case 871:
        case 873:
            return 1;
    }
    return 0;
}

FR_STATIC int __nlscharacter_getratio(int i_p1, int i_p2)
{
    int i = -1;
    int j = 0;
    int k = (sizeof m_maxCharWidth)/(sizeof(int)*2) - 1;

    if (i_p2 == i_p1)
        return 1;
    int m;
    while (j <= k) {
        m = (j + k) / 2;
        if (i_p1 < m_maxCharWidth[m][0]) {
            k = m - 1;
        } else if (i_p1 > m_maxCharWidth[m][0]) {
            j = m + 1;
        } else {
            i = m;
            break;
        }
    }
    if (i < 0) {
        return 0;
    }

    int n = -1;
    j = 0;
    k = (sizeof m_maxCharWidth)/(sizeof(int)*2) - 1;
    while (j <= k) {
        m = (j + k) / 2;
        if (i_p2 < m_maxCharWidth[m][0]) {
            k = m - 1;
        } else if (i_p2 > m_maxCharWidth[m][0]) {
            j = m + 1;
        } else {
            n = m;
            break;
        }
    }
    if (n < 0) {
        return 0;
    }
    int i1 = m_maxCharWidth[i][1] & 0xFF;
    if (i1 == 1) {
        return 1;
    }
    if (m_maxCharWidth[n][1] >> 8 == 0) {
        return i1;
    }

    int i2 = m_maxCharWidth[n][1] & 0xFF;
    int i3 = i1 / i2;
    if (i1 % i2 != 0)
        i3++;
    return i3;
}

/*******************************************************************************
*  FUNC     :  解析pro包响应
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_sess                           (Oracle私有的session)
*           :  *p_st_ora_packet                     (Oracle数据包)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int ora_tti_pro_unpack_resp(Conntrack *p_st_conn
                            , L7session_ora *p_st_sess
                            , L7_ora_packet *p_st_ora_packet)
{
    short s_tmp = 0;
    short s_pro_srv_version = 0;
    short s_oracle_version = 0;
    short s_srv_charset = 0;
    short s_srv_flags = 0;
    short s_srv_charset_elem;
    char *p_srv_compile_time_capabilities = NULL;
    short s_srv_compile_time_capabilities_len = 0;
    int i_ttc_type_rep_tmp;
    int i_len1 = 0;
    int i_len2 = 0;
    char *p_nchar_charset_str = NULL;
    short s_nchar_charset;
    int i_loop;
    short s_len_tmp = 0;
//    char *p_runtime_capabilities = NULL; //未使用到这个值，所以不赋值也没关系
    short s_driver_charset = 0;

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_tmp) == FR_FAIL)
        goto error;

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_pro_srv_version) == FR_FAIL) // proSvrVer
        goto error;
    p_st_sess->c_pro_srv_version = (char) s_pro_srv_version;
    switch (s_pro_srv_version)
    {
        case 4:
            s_oracle_version = 7230;
            break;
        case 5:
            s_oracle_version = 8030;
            break;
        case 6:
            s_oracle_version = 8100;
            break;
        default:
            goto error;
    }
    p_st_sess->s_ora_version = s_oracle_version;

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_tmp) == FR_FAIL) // 跳过
        goto error;

    if (ora_mar_skipTEXT(APM_ORA_PROSVRSTR_MAXLEN, p_st_ora_packet) != FR_SUCC) // proSvrStr
        goto error;

    if (ora_mar_unmarshalSB2(p_st_sess, p_st_ora_packet, &s_srv_charset) == FR_FAIL) // svrCharSet
        goto error;
    p_st_sess->s_srv_charset = s_srv_charset;

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_srv_flags) == FR_FAIL) // svrFlags
        goto error;

    if (ora_mar_unmarshalSB2(p_st_sess, p_st_ora_packet, &s_srv_charset_elem) == FR_FAIL) // svrCharSetElem
        goto error;
    if (s_srv_charset_elem > 0)
    {
        if (ora_mar_skipNBytes_by_len(s_srv_charset_elem * 5, p_st_ora_packet) != FR_SUCC) // 跳过
            goto error;
    }

    if (s_pro_srv_version < 5)
        goto success;

    i_ttc_type_rep_tmp = ora_get_ttc_type_rep(p_st_sess, 1);
    ora_set_ttc_type_rep(p_st_sess, 1, 0);

    if (ora_mar_unmarshalUB2(p_st_sess, p_st_ora_packet, &i_len1) == FR_FAIL) // i
        goto error;

    if (p_st_sess->i_cli_is_bigendian == FR_YES
            || p_st_sess->i_use_oci == FR_YES // ./apm -n -t1 -d /mnt/hgfs/intellij14/apm/global/pcap/data_dev/oracle/aix/sqlplus_192.168.18.239_11.2.0.1_a_windows_2_192.168.18.228_11.2.0.1.0_w.pcap
            )
        ora_set_ttc_type_rep(p_st_sess, 1, 1);
    else
        ora_set_ttc_type_rep(p_st_sess, 1, i_ttc_type_rep_tmp);
    if (ora_mar_unmarshalNBytes_by_len(i_len1, p_st_ora_packet, &p_nchar_charset_str) == FR_FAIL) // arrayOfByte1
        goto error;

    i_len2 = 6 + (p_nchar_charset_str[5] & 0xFF) + (p_nchar_charset_str[6] & 0xFF);
    s_nchar_charset = (short)((p_nchar_charset_str[(i_len2 + 3)] & 0xFF) << 8);
    s_nchar_charset = (short)(s_nchar_charset | (short)(p_nchar_charset_str[(i_len2 + 4)] & 0xFF));

    ora_free((void **) &p_nchar_charset_str);

    if (s_pro_srv_version < 6)
        goto success;

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_srv_compile_time_capabilities_len) == FR_FAIL) // k
        goto error;
    p_st_sess->s_srv_compile_time_capabilities_len = s_srv_compile_time_capabilities_len;
    if (s_srv_compile_time_capabilities_len > 0)
    {
        if ((p_srv_compile_time_capabilities = (char *) ora_malloc(s_srv_compile_time_capabilities_len * sizeof(char), bytebuff_remaining(p_st_ora_packet->p_st_packet))) == NULL)
        {
            ORA_MALLOCK_ERR;
            goto error;
        }
        for (i_loop = 0; i_loop < s_srv_compile_time_capabilities_len; i_loop++)
        {
            if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_tmp) == FR_FAIL)
                goto error;
            p_srv_compile_time_capabilities[i_loop] = (char) s_tmp;
        }
        ora_free((void **) &(p_st_sess->p_srv_compile_time_capabilities));
        p_st_sess->p_srv_compile_time_capabilities = p_srv_compile_time_capabilities;
    }

    if (ora_mar_unmarshalUB1(p_st_ora_packet, &s_len_tmp) == FR_FAIL)
        goto error;
    if (s_len_tmp > 0)
    {
        if (ora_mar_skipB1Array(s_len_tmp, p_st_ora_packet) != FR_SUCC) // runtime_capabilities
            goto error;
    }

success:
    s_driver_charset = __find_driver_charset(s_srv_charset, s_oracle_version);

    p_st_sess->i_c2snlsratio = __nlscharacter_getratio(s_srv_charset, s_driver_charset);

    p_st_sess->i_srv_conversion = s_driver_charset != s_srv_charset ? FR_YES : FR_NO;
    if (__is_charset_multibyte(s_driver_charset))
    {
        if (__is_charset_multibyte(s_srv_charset))
            p_st_sess->c_conversion_flags = 1;
        else
            p_st_sess->c_conversion_flags = 2;
    }
    else
        p_st_sess->c_conversion_flags = (char) s_srv_flags;

    p_st_sess->i_CHR_conversion_flags = (p_st_sess->c_conversion_flags & 0x2) > 0 ? FR_YES : FR_NO;

    if (((p_st_sess->i_use_special_field == FR_NO && p_st_sess->s_tns_version > 310) || (p_st_sess->i_use_special_field == FR_YES && p_st_sess->s_tns_version > 314))
            && s_srv_compile_time_capabilities_len > 37 && (p_srv_compile_time_capabilities[37] & 32) == 32)
        p_st_sess->i_useCLR_big_chunks = FR_YES;
    else
        p_st_sess->i_useCLR_big_chunks = FR_NO;

    if (s_srv_compile_time_capabilities_len > 7)
    {
        p_st_sess->c_ttc_version = p_srv_compile_time_capabilities[7];
        ORA_PRINT_ALERT("ttc version first:[%d]\n", p_st_sess->c_ttc_version);
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}
